package ktvdownloadserver

import (
	//"encoding/binary"
	"bytes"
	"encoding/binary"
	"ktvserver/core/session"
	"ktvserver/core/tcpserver"
	"ktvserver/ktvutil"
	"ktvserver/ktvutil/videopath"
	"log"
	"net"
	"os"
	"path/filepath"
	//"syscall"
)

/*
typedef struct _WIN32_FIND_DATAW {
    DWORD dwFileAttributes;
    FILETIME ftCreationTime;
    FILETIME ftLastAccessTime;
    FILETIME ftLastWriteTime;
    DWORD nFileSizeHigh;
    DWORD nFileSizeLow;
    DWORD dwReserved0;
    DWORD dwReserved1;
    WCHAR  cFileName[ MAX_PATH ];
    WCHAR  cAlternateFileName[ 14 ];
#ifdef _MAC
    DWORD dwFileType;
    DWORD dwCreatorType;
    WORD  wFinderFlags;
#endif
} WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW;
*/

// WIN32查找文件时的数据
type WIN32FindDataW struct {
	FileAttributes    uint32
	CreationTime      uint64
	LastAccessTime    uint64
	LastWriteTime     uint64
	FileSizeHigh      uint32
	FileSizeLow       uint32
	Reserved0         uint32
	Reserved1         uint32
	FileName          [260]uint16
	AlternateFileName [14]uint16
}

func GetPublicFileListData(folder string) ([]byte, int) {
	// 创建新的缓冲区
	buf := new(bytes.Buffer)

	//wt := buf.GetWriter()

	// 打开文件夹
	f, err := os.Open(folder)
	if err != nil {
		return buf.Bytes(), 0
	}

	// 获取该文件夹下的所有文件
	count := 0

	for {
		dirs, err := f.Readdir(100)
		if err != nil || len(dirs) == 0 {
			break
		}
		for _, d := range dirs {
			name := d.Name()
			if d.IsDir() {
				//name += "/"
				// 忽略所有的文件夹
				continue
			}

			//
			var dat WIN32FindDataW
			dat.FileAttributes = 0
			dat.CreationTime = 0
			dat.LastAccessTime = 0
			dat.LastWriteTime = 0
			dat.FileSizeHigh = uint32(d.Size() >> 32) // 取出高位
			dat.FileSizeLow = uint32(d.Size())        // 低位
			dat.Reserved0 = 0
			dat.Reserved1 = 0

			// 文件名转换为UTF16编码
			filename := ktvutil.StringToUTF16(name)
			for i := 0; i < len(name); i++ {
				dat.FileName[i] = filename[i]
			}

			// 写入二进制数据
			binary.Write(buf, binary.LittleEndian, &dat)
			count++
		} // end dirs
	} // end for

	return buf.Bytes(), count
}

// 下载服务器获取文件列表
func KtvPublicSongFileList(w tcpserver.ResponseWriter, req *tcpserver.Request) {

	//fmt.Println("KtvPublicSongFileList enter ...")
	//defer fmt.Println("KtvPublicSongFileList leave ...")

	info := tcpserver.GetKtvRequestInfo(req.DataBody)

	strSongFolder := videopath.GetPublicSongFolder(info.Number)

	if strSongFolder == "" {
		// 找不到歌曲文件夹
		tcpserver.WriteHeader(w, 0, 0)
		tcpserver.WriteFooter(w)
		return
	}

	//  检查歌曲文件夹是否存在
	_, err := os.Stat(strSongFolder)
	if err != nil {
		// 找不到文件直接发送空数据
		log.Printf("public song folder not found, number=%d, path=%s", info.Number, strSongFolder)
		tcpserver.WriteHeader(w, 0, 0)
		tcpserver.WriteFooter(w)
		return
	}

	// 查找文件列表
	data, count := GetPublicFileListData(strSongFolder)
	if len(data) == 0 {
		// 找不到文件
		log.Printf("public song file list count=%d, number=%d", count, info.Number)
		tcpserver.WriteHeader(w, 0, 0)
		tcpserver.WriteFooter(w)
		return
	} else {
		// 发送文件列表数据
		tcpserver.WriteHeader(w, uint32(len(data)), 0)
		w.Write(data)
		tcpserver.WriteFooter(w)

	}

}

// 下载服务器获取文件数据
func KtvPublicSongFileData(w tcpserver.ResponseWriter, req *tcpserver.Request) {

	//fmt.Println("KtvPublicSongFileData enter ...")
	//defer fmt.Println("KtvPublicSongFileData leave ...")

	info := tcpserver.GetKtvRequestInfo(req.DataBody)

	// 获取下载令牌
	token := uint64(info.FastForward) | uint64(info.FFLength)<<32
	req.SetToken(token)

	// 查找下载的文件夹
	strSongFolder := videopath.GetPublicSongFolder(info.Number)

	if strSongFolder == "" {
		// 找不到歌曲文件夹
		log.Printf("(%v) GetPublicSongFileData, invalid folder, number=%d", req.RemoteIP(), info.Number)
		tcpserver.WriteHeader(w, 0, 0)
		tcpserver.WriteFooter(w)
		return
	}

	// 获取歌曲视频文件路径
	strFileName := ktvutil.UTF16ToString(info.FileName[:])
	strFilePath := filepath.Join(strSongFolder, strFileName)

	// 获取文件的信息
	fi, err := os.Stat(strFilePath)
	if err != nil || fi.Size() <= 0 {
		// 找不到文件直接发送空数据
		tcpserver.WriteHeader(w, 0, 0)
		tcpserver.WriteFooter(w)
		return
	}

	// 计算发送的长度
	offset, length := tcpserver.CalcuteTransferParam(fi.Size(), int64(info.Start), int64(info.Length))

	// 打印显示信息
	if offset == 0 {
		log.Printf("(%v) GetPublicSongData, number=%d, filename=%s, offset=%d, len=%d",
			req.RemoteIP(),
			info.Number,
			strFileName,
			offset,
			length)
	}

	// 限速标识
	isSpeedLimited, newSpeed := session.GetSpeedLimitInfo(token)

	// 发送文件数据
	if isSpeedLimited == false {
		// 不受限发送数据
		tcpserver.WriteHeader(w, uint32(length), 0)
		tcpserver.WriteFile(w, strFilePath, offset, length)
		tcpserver.WriteFooter(w)
	} else {
		// 限制发送速度
		tcpserver.WriteHeader(w, uint32(length), 0)
		tcpserver.WriteFileAtSpeed(w, strFilePath, offset, length, int64(newSpeed*1024))
		tcpserver.WriteFooter(w)
	}

}

// 下载地址分配
func KtvPublicSongIP(w tcpserver.ResponseWriter, req *tcpserver.Request) {

	ktvinfo := tcpserver.GetKtvRequestInfo(req.DataBody)

	// 从filename位置复制8字节出来
	//tokenData := ktvinfo.FileName[0:4]

	// 检查下载的session是否有效
	var token uint64
	buf := new(bytes.Buffer)
	binary.Write(buf, binary.LittleEndian, ktvinfo.FileName[0:4])
	binary.Read(buf, binary.LittleEndian, &token)

	/*
		if session.IsAllow(token) != true {
			//	log.Printf("Invalid token")
			log.Printf("(%v) GetPublicSongIP, invalid token, number=%d", req.RemoteIP(), ktvinfo.Number)
			tcpserver.WriteHeader(w, 0, session.KTV_SESSION_STATUS_INVALID)
			tcpserver.WriteFooter(w)
			return
		}
	*/

	// 记录到下载列表
	session.LogDownloadSongNum(token, ktvinfo.Number)
	req.SetToken(token)

	// 统计下载次数
	ktvutil.IncDonwloadSongCount()

	// 返回下载地址
	var ipinfo SongIpList

	ipinfo.SongNum = ktvinfo.Number
	ipinfo.Count = 1
	ipinfo.Port = uint16(ktvutil.GetDownloadServerPort())
	// 默认127.0.0.1
	ipinfo.IP[0] = uint32(127)<<24 | uint32(0)<<16 | uint32(0)<<8 | uint32(1)

	// 本地连接的地址
	localip := net.ParseIP(req.LocalIP()).To4()
	//log.Printf("localip=%s, v=%v", req.LocalIP(), localip)
	if localip != nil {
		ipinfo.IP[0] = uint32(localip[0])<<24 | uint32(localip[1])<<16 | uint32(localip[2])<<8 | uint32(localip[3])
	}

	// 打印显示信息
	log.Printf("(%v) GetPublicSongIP, number=%d, ip=%v:%d",
		req.RemoteIP(),
		ktvinfo.Number,
		localip,
		ipinfo.Port)

	tcpserver.WriteHeader(w, uint32(ipinfo.StructSize()), 0)
	binary.Write(w, binary.LittleEndian, &ipinfo)
	tcpserver.WriteFooter(w)

}
